package com.example.sfm.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.sfm.exception.ObjectException;
import com.example.sfm.mapper.*;
import com.example.sfm.pojo.dto.GradeStatisticsDto;
import com.example.sfm.pojo.dto.GradesDto;
import com.example.sfm.pojo.dto.GradesImportDto;
import com.example.sfm.pojo.dto.GradesQueryDto;
import com.example.sfm.pojo.entity.Courses;
import com.example.sfm.pojo.entity.Grades;
import com.example.sfm.pojo.entity.StudentRecords;
import com.example.sfm.pojo.vo.GradeStatisticsVo;
import com.example.sfm.pojo.vo.GradesExportVo;
import com.example.sfm.pojo.vo.GradesVo;
import com.example.sfm.result.PageResult;
import com.example.sfm.service.IGradesService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.sfm.utils.ThreadLocalUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author sfm
 * @since 2025-03-07
 */
@Service
@RequiredArgsConstructor
public class GradesServiceImpl extends ServiceImpl<GradesMapper, Grades> implements IGradesService {

    private final UsersMapper usersMapper;
    private final CoursesMapper coursesMapper;
    private final StudentRecordsMapper recordsMapper;

    @Override
    public void createGrade(GradesDto dto) {
        Grades entity = BeanUtil.copyProperties(dto, Grades.class).setGrade(toGrade(dto.getScore()));
        if (!save(entity)) {
            throw new ObjectException("创建成绩失败！");
        }
    }

    @Override
    public GradeStatisticsVo getGradeStatistics(@NotNull GradeStatisticsDto dto) {
        dto.setTeacherId(ThreadLocalUtil.getId());
        List<Integer> courseIds = coursesMapper.selectList(
                new LambdaQueryWrapper<Courses>().eq(Courses::getTeacherId, dto.getTeacherId())
        ).stream().map(Courses::getId).collect(Collectors.toList());

        if (courseIds.isEmpty()) {
            throw new ObjectException("该教师没有负责任何课程");
        }

        Integer courseId = dto.getCourseId();

        if (courseId != null) {
            if (!courseIds.contains(courseId)) {
                throw new ObjectException("该教师不负责此课程");
            }
            courseIds = courseIds.stream().filter(id -> id.equals(courseId)).collect(Collectors.toList());
        }

        // 查询对应课程的成绩记录
        LambdaQueryWrapper<Grades> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(Grades::getCourseId, courseIds);
        List<Grades> gradeList = baseMapper.selectList(queryWrapper);

        if (gradeList == null || gradeList.isEmpty()) {
            throw new ObjectException("没有找到相关成绩数据");
        }

        // 统计数据：计算总分、最高分、最低分、及格数
        double total = 0, max = Double.MIN_VALUE, min = Double.MAX_VALUE;
        int passCount = 0;
        for (Grades grade : gradeList) {
            double score = grade.getScore().doubleValue();
            total += score;
            if (score > max) {
                max = score;
            }
            if (score < min) {
                min = score;
            }
            if (score >= 60) { // 假设60分为及格线
                passCount++;
            }
        }
        int count = gradeList.size();
        double average = total / count;
        double passRate = ((double) passCount / count) * 100;

        GradeStatisticsVo vo = new GradeStatisticsVo();
        vo.setAverageScore(average);
        vo.setMaxScore(max);
        vo.setMinScore(min);
        vo.setPassRate(passRate);
        return vo;
    }

    @Override
    public PageResult<GradesVo> listGrades(@NotNull GradesQueryDto dto) {
        LambdaQueryWrapper<Grades> wrapper = new LambdaQueryWrapper<Grades>()
                .eq(dto.getStudentId() != null, Grades::getStudentId, dto.getStudentId())
                .eq(dto.getCourseId() != null, Grades::getCourseId, dto.getCourseId())
                .eq(dto.getGrade() != null, Grades::getGrade, dto.getGrade())
                .ge(dto.getExamDateStart() != null, Grades::getExamDate, dto.getExamDateStart())
                .le(dto.getExamDateEnd() != null, Grades::getExamDate, dto.getExamDateEnd())
                .orderByDesc(Grades::getExamDate);
        if (StrUtil.isNotBlank(dto.getFullName())) {
            wrapper.inSql(Grades::getStudentId,
                    "SELECT id FROM users WHERE full_name LIKE '%" + dto.getFullName() + "%' AND role = 2");
        }
        if (StrUtil.isNotBlank(dto.getSemester())) {
            wrapper.inSql(Grades::getCourseId,
                    "SELECT id FROM courses WHERE semester = '" + dto.getSemester() + "'");
        }
        Page<Grades> page = page(new Page<>(dto.getPageNum(), dto.getPageSize()), wrapper);
        return new PageResult<>(page.getTotal(),
                page.getRecords().stream()
                        .map(g -> BeanUtil.copyProperties(g, GradesVo.class)
                                .setStudentName(usersMapper.selectById(g.getStudentId()).getFullName())
                                .setCourseName(coursesMapper.selectById(g.getCourseId()).getCourseName()))
                        .toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateGrade(Integer id, GradesDto dto) {
        Grades entity = getById(id);
        if (entity == null) throw new ObjectException("成绩记录不存在");

        BeanUtil.copyProperties(dto, entity);
        if (!updateById(entity)) {
            throw new ObjectException("更新成绩失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteGrades(List<Integer> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            throw new ObjectException("ID列表不能为空");
        }
        List<Integer> distinctIds = ids.stream().distinct().toList();
        if (!removeBatchByIds(distinctIds)) {
            throw new ObjectException("删除成绩失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importFromExcel(@NotNull MultipartFile file) {
        try {
            List<Grades> gradesList = EasyExcel.read(file.getInputStream())
                    .head(GradesImportDto.class)
                    .sheet()
                    .doReadSync()
                    .stream()
                    .map(obj -> convertToEntity((GradesImportDto) obj))
                    .toList();

            if (!saveBatch(gradesList)) {
                throw new ObjectException("批量导入成绩失败");
            }
        } catch (IOException e) {
            throw new ObjectException("文件读取失败");
        }
    }

    @Override
    public void exportExcel(@NotNull HttpServletResponse response, GradesQueryDto dto) {
        try {
            List<GradesVo> data = listGrades(dto).getItems(); // 复用查询逻辑
            String fileName = URLEncoder.encode("成绩数据_" + LocalDate.now(), StandardCharsets.UTF_8);

            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

            EasyExcel.write(response.getOutputStream(), GradesExportVo.class)
                    .sheet("成绩数据")
                    .doWrite(data.stream()
                            .map(this::convertToExportVo)
                            .toList());
        } catch (IOException e) {
            throw new ObjectException("导出文件失败");
        }
    }

    private @NotNull Grades convertToEntity(@NotNull GradesImportDto importDto) {

        BigDecimal score = importDto.getScore();
        if (score.compareTo(BigDecimal.ZERO) < 0 || score.compareTo(BigDecimal.valueOf(100)) > 0) {
            throw new ObjectException("成绩范围错误: " + score);
        }


        StudentRecords studentRecord = recordsMapper.selectOne(
                new LambdaQueryWrapper<StudentRecords>()
                        .eq(StudentRecords::getStudentNumber, importDto.getStudentNumber())
        );
        if (studentRecord == null) {
            throw new ObjectException("学生记录不存在: " + importDto.getStudentNumber());
        }


        Courses course = coursesMapper.selectOne(
                new LambdaQueryWrapper<Courses>()
                        .eq(Courses::getCourseCode, importDto.getCourseCode())
        );
        if (course == null) {
            throw new ObjectException("课程不存在: " + importDto.getCourseCode());
        }


        Grades grades = BeanUtil.copyProperties(importDto, Grades.class);
        grades.setStudentId(studentRecord.getUserId())
                .setCourseId(course.getId())
                .setGrade(toGrade(score));

        return grades;
    }


    private @NotNull Integer toGrade(@NotNull BigDecimal c) {
        if (c.compareTo(new BigDecimal("90")) >= 0) {
            return 3;
        } else if (c.compareTo(new BigDecimal("80")) >= 0) {
            return 2;
        } else if (c.compareTo(new BigDecimal("70")) >= 0) {
            return 1;
        } else {
            return 0;
        }
    }

    private @NotNull @Unmodifiable GradesExportVo convertToExportVo(GradesVo vo) {
        GradesExportVo gradesExportVo = BeanUtil.copyProperties(vo, GradesExportVo.class);
        gradesExportVo.setGradeName(convertGradeLevel(vo.getGrade()));
        gradesExportVo.setStudentNumber(recordsMapper.selectOne(new LambdaQueryWrapper<StudentRecords>().eq(StudentRecords::getUserId,vo.getStudentId())).getStudentNumber());
        Courses courses = coursesMapper.selectById(vo.getCourseId());
        gradesExportVo.setCourseCode(courses.getCourseCode());
        gradesExportVo.setCourseName(courses.getCourseName());
        return gradesExportVo;
    }

    @Contract(pure = true)
    private String convertGradeLevel(@NotNull Integer grade) {
        return switch (grade) {
            case 0 -> "不及格";
            case 1 -> "及格";
            case 2 -> "良好";
            case 3 -> "优秀";
            default -> "未知";
        };
    }
}
